using CfgTable;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.GamePlay;
using NetProtocol.Enum;
using NetProtocol.POD;
using System.Collections.Generic;

namespace IQIGame.Onigao.Logic
{
    public class LevelHeroEntity : BaseEntity
    {
        #region fields
        /// <summary>
        /// 英雄所在的房间
        /// 房间实体并非占用状态，需特殊纪录
        /// 和电梯不同，在房间里，英雄仍能和NPC交互，由于没有寻路数据，所以能否交互只判断英雄和实体是否属于同一个房间
        /// 但电梯内英雄不能进行任何交互，不能触发任何服务，所以电梯直接使用占用状态纪录即可。
        /// </summary>
        public LevelEntity houseEntity;
        /// <summary>
        /// 进入物理寻路时的位置
        /// </summary>
        public Vector3Logic enterPhysicAreaPos;
        public LevelPlayer player { get; set; }
        /// <summary>
        /// 在AOI范围内的实体集合，但由于删除范围更大，所以这个集合实际上代表的是视图层有的实体数据（包含了一部分在aoi范围外但还没有超出删除范围的实体）
        /// </summary>
        public HashSet<LevelEntity> aoiEntities = new HashSet<LevelEntity>();

        private Dictionary<int, float> _entityRangeLvMapPrev = new Dictionary<int, float>();
        private Dictionary<int, float> _entityRangeLvMapNow = new Dictionary<int, float>();
        /// <summary>
        /// 当前所处区域的虚拟实体ID
        /// </summary>
        private int _areaEntityId;
        #endregion fields

        #region properties
        public Vector3Logic obbSize { get; } = new Vector3Logic(10, 100, 10);
        public CfgPhysicalEntity cfgEntity { get; private set; }
        /// <summary>
        /// 是否不可探测的，关卡中队伍/玩家化身如果进入死亡，待机等待动画等状态时，会处于不可探测的状态。
        /// 注意这里的不可探测通常是指不被怪物，机关等实体探测。
        /// </summary>
        public bool isUndetectable => player.isUndetectable;

        protected override bool isNavAgent => true;
        public LevelEntity areaEntity => region.GetEntityById(_areaEntityId);
        #endregion properties

        public static LevelHeroEntity CreateFromPool(int id)
        {
            LevelHeroEntity entity = ClassPool.Get<LevelHeroEntity>();
            entity.id = id;
            return entity;
        }

        public static LevelHeroEntity CreateFromPool(LevelEntityPOD entityPOD)
        {
            LevelHeroEntity entity = CreateFromPool(entityPOD.id);
            entity.position = new Vector3Logic(entityPOD.x, entityPOD.y, entityPOD.z);
            return entity;
        }

        public void Init(LevelPlayer player, int skinCid)
        {
            this.player = player;
            CfgHeroSkin skin = TableCenter.heroSkin.Get(skinCid);
            CfgPhysicalEntity cfgEntity = TableCenter.physicalEntity.Get(skin.LevelEntity);
            this.cfgEntity = cfgEntity;
            isActive = true;
            defaultMoveSpeed = cfgEntity.DefaultSpeed;
        }

        public void SetRegion(LevelRegion region)
        {
            this._region = region;
        }

        public override void LogicOnUpdate()
        {

        }

        /// <summary>
        /// 传送
        /// </summary>
        /// <param name="targetPosition"></param>
        /// <returns></returns>
        public bool Teleport(Vector3Logic targetPosition)
        {
            return TeleportInNavMesh(true, targetPosition);
        }

        /// <summary>
        /// 处理客户端发送的移动请求
        /// 由于分物理寻路和网格寻路，所以分情况看直接更新位置还是调用在网格内移动的方法
        /// </summary>
        /// <param name="targetPosition"></param>
        /// <returns>是否是合法移动</returns>
        public bool ClientMove(Vector3Logic targetPosition)
        {
            //物理移动检测
            if (navigator.isPhysical)
            {
                if (navigator.IsInPhysicalArea(targetPosition))
                {
                    UpdateMove(targetPosition, player.leaderHero.buffSpeedRatio, false, EntityMoveDirType.KeepVertical);
                    return true;
                }
                return false;
            }
            //网格寻路检测
            //Vector3Logic lastPos = position;
            if (MoveInNavMesh(targetPosition))
            {
                ////客户端认为可以行走并请求的位置，明显超出底层网格判定的位置。
                ////虽然网格认为行走合法（例如在悬崖边不断尝试往外走），但服务器和客户端的判断有较大偏差，强制认为不合法
                //if ((targetPosition - position).sqrMagnitude - (position - lastPos).sqrMagnitude > 200)
                //{
                //    LogicLog.LogWarning($"英雄网格移动失败，网格判定的距离太短。移动前位置：{lastPos}，网格计算的合法位置：{position}，客户端同步的位置：{targetPosition}");
                //    return false;
                //}
                return true;
            }
            LogicLog.LogWarning("英雄网格移动失败");
            return false;
        }

        /// <summary>
        /// 在寻路网格中移动（移动主要方法）
        /// </summary>
        /// <param name="position"></param>
        /// <returns></returns>
        public bool MoveInNavMesh(Vector3Logic position)
        {
            return MoveInNavMesh(position, player.leaderHero.buffSpeedRatio, true, EntityMoveDirType.KeepVertical);
        }

        /// <summary>
        /// 更新位置，执行和位置有关的逻辑
        /// 注意网格寻路的计算应该在该方法调用之前
        /// </summary>
        /// <param name="position"></param>
        /// <param name="speedRatio"></param>
        /// <param name="needSync"></param>
        /// <param name="dirType"></param>
        /// <returns></returns>
        public override bool UpdateMove(Vector3Logic position, float speedRatio, bool needSync, EntityMoveDirType dirType = EntityMoveDirType.KeepVertical)
        {
            return base.UpdateMove(position, speedRatio, needSync, dirType);
        }

        /// <summary>
        /// 缓存移动前，上一次英雄实体的位置
        /// 用于判断英雄此次移动的方位，例如判断英雄是否正好从某个范围离开
        /// </summary>
        public void EntityDisNowToPrev()
        {
            _entityRangeLvMapPrev.Clear();
            foreach (var kv in _entityRangeLvMapNow)
            {
                _entityRangeLvMapPrev.Add(kv.Key, kv.Value);
            }
            _entityRangeLvMapNow.Clear();
        }

        public void UpdateEntityDisForTrig(int entityId, float sqrDistance)
        {
            _entityRangeLvMapNow.Add(entityId, sqrDistance);
        }

        /// <summary>
        /// 获取上一次指定实体和英雄的距离（仅针对触发器有效）
        /// </summary>
        public float GetPrevEntityDisFromMe(int entityId)
        {
            if (_entityRangeLvMapPrev.TryGetValue(entityId, out float sqrDis))
            {
                return sqrDis;
            }
            return -1;
        }

        public void SetAreaEntityId(int value)
        {
            _areaEntityId = value;
        }

        public bool AddAOIEntity(LevelEntity entity)
        {
            if (entity == null)
            {
                return false;
            }
            if (aoiEntities.Add(entity))
            {
                entity.OnAOIAdd(player.id);
                return true;
            }
            return false;
        }

        public bool RemoveAOIEntity(LevelEntity entity)
        {
            if (entity == null)
            {
                return false;
            }
            if (aoiEntities.Remove(entity))
            {
                entity.OnAOIRemove(player.id);
                return true;
            }
            return false;
        }

        public override IPOD GeneratePOD()
        {
            LevelEntityPOD pod = new LevelEntityPOD();
            pod.id = this.id;
            pod.x = (int)this.position.x;
            pod.y = (int)this.position.y;
            pod.z = (int)this.position.z;
            pod.type = LevelEntityType.Hero;
            LevelHeroEntityPOD heroPOD = new LevelHeroEntityPOD();
            heroPOD.playerId = player.logicPlayer.id;
            pod.hero = heroPOD;
            return pod;
        }

        public override void OnReset()
        {
            base.OnReset();
            player = null;
            cfgEntity = null;
            aoiEntities.Clear();
            _entityRangeLvMapPrev.Clear();
            _entityRangeLvMapNow.Clear();
        }
    }
}
